package gov.house.louise.gmg;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Date;
import java.util.HashMap;

//From http://unserializableone.blogspot.com/2007/08/lightweight-google-geocoder-with-java.html

public class Geocoder {
	private final static String ENCODING = "UTF-8";
	// private final static String KEY =
	// "ABQIAAAA4n9txRJg2WoavxKJerCj9xQdGEd_UqcYqKvNUxuZ719m_69iBRQYS6bRHoWgErO6GrZYLMaEtWVgww";
	private String key;
	private HashMap<String, double[]> cache;
	private long lastQueryTime;

	public Geocoder() {
		this.cache = new HashMap<String, double[]>();
		this.lastQueryTime = 0;
		this.loadCache(new File("geocache.tmp"));
	}

	public void saveCache(File file) {
		try {
			FileOutputStream fos = new FileOutputStream(file);
			ObjectOutputStream oos = new ObjectOutputStream(fos);
			oos.writeObject(this.cache);
			oos.writeObject(this.key);
			oos.close();
			fos.close();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void loadCache(File file) {
		if (file.exists()) {
			try {
				FileInputStream fis = new FileInputStream(file);
				ObjectInputStream ois = new ObjectInputStream(fis);
				this.cache = (HashMap<String, double[]>) ois.readObject();
				this.key = (String) ois.readObject();
				ois.close();
				fis.close();
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				//If there's and EOF error it is probably just an older cache file without the key stored

				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} 
		}
	}

	private String formatAddress(String street, String city, String state,
			String zip) {
		return street + " " + city + ", " + state + " " + zip;
	}

	public double[] LookupAddress(String street, String city, String state,
			String zip) {
		if(zip.length() == 9) zip = zip.substring(0, 5) + "-" + zip.substring(5,9); //9 digit zips need to be formatted xxxxx-xxxx
		String address = this.formatAddress(street, city, state, zip);
		if (cache.containsKey(address)) {
			System.out.println("Address cached");
			return cache.get(address);
		}
		// Check if we need to pause to avoid clobbering Google
		if (this.lastQueryTime > 0) {
			long now = new Date().getTime();
			long pause = 500 - (now - this.lastQueryTime);
			if (pause > 0)
				try {
					System.out.println("Pausing for " + String.valueOf(pause)
							+ "ms");
					Thread.sleep(pause);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
		}

		double[] latLong = new double[2];
		int statusCode = -1;
		try {
			BufferedReader reader = new BufferedReader(new InputStreamReader(
					new URL("http://maps.google.com/maps/geo?q="
							+ URLEncoder.encode(address, Geocoder.ENCODING)
							+ "&output=csv&key=" + this.key).openStream()));
			this.lastQueryTime = new Date().getTime();
			String line;
			while ((line = reader.readLine()) != null) {
				// Format: 200,6,42.730070,-73.690570
				statusCode = Integer.parseInt(line.substring(0, 3));
				if (statusCode == 200)
					// Returned ok,
					latLong[0] = Double.parseDouble(line.substring("200,6,"
							.length(), line.indexOf(',', "200,6,".length())));
				latLong[1] = Double.parseDouble(line.substring(line.indexOf(
						',', "200,6,".length()) + 1, line.length()));
				cache.put(address, latLong);
				return latLong;
			}
			if (statusCode != 200) {
				switch (statusCode) {
				case 400:
					throw new IOException("Bad Request");
				case 500:
					throw new IOException("Unknown error from Google Encoder");
				case 601:
					throw new IOException("Missing query");
				case 602:
					return null;
				case 603:
					throw new IOException("Legal problem");
				case 604:
					throw new IOException("No route");
				case 610:
					throw new IOException("Bad key");
				case 620:
					throw new IOException("Too many queries");
				}
			}
			return null;

		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}

	}

	public void setKey(String key) {
		this.key = key;
	}

	public String getKey() {
		return key;
	}
}
